package com.zkjl.protect_business.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.google.common.collect.Lists;
import com.zkjl.protect_business.common.BaseException;
import com.zkjl.protect_business.common.UserThreadLocal;
import com.zkjl.protect_business.dto.businessType.BusinessDTO;
import com.zkjl.protect_business.dto.businessType.SpecifiedFieldDTO;
import com.zkjl.protect_business.dto.businessType.UpdateBusinessDTO;
import com.zkjl.protect_business.entity.*;
import com.zkjl.protect_business.repository.BusinessRepository;
import com.zkjl.protect_business.repository.TemplateRepository;
import com.zkjl.protect_business.service.*;
import com.zkjl.protect_business.utils.QueryUtil;
import com.zkjl.protect_business.vo.BusinessVO;
import com.zkjl.protect_business.vo.statistic.GroupVO;
import com.zkjl.protect_business.vo.statistic.OrgStatDateVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.BulkByScrollTask;
import org.elasticsearch.index.reindex.ReindexRequest;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.zkjl.protect_business.common.Consts.*;
import static com.zkjl.protect_business.common.Status.*;
import static org.apache.poi.ss.usermodel.CellType.ERROR;

@Service
@Slf4j
public class BusinessServiceImpl implements BusinessService {

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    BusinessRepository businessRepository;
    @Autowired
    OrganizationService organizationService;
    @Autowired
    ModelMapper modelMapper;
    @Autowired
    LogService logService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    PermissionService permissionService;
    @Autowired
    TemplateRepository templateRepository;
    @Autowired
    ArchiveService archiveService;
    @Autowired
    DocumentService documentService;

    @Autowired
    RestHighLevelClient client;

    @Override
    public Business save(BusinessDTO businessDTO) {

        Business business = modelMapper.map(businessDTO, Business.class);
        String id = IdUtil.simpleUUID();
        business.setId(id);
        business.setBaseInfo();
        String pid = business.getPid();
        String name = business.getName();

        List<Business> allByName = businessRepository.findAllByNameAndPid(name, pid);
        if (CollUtil.isNotEmpty(allByName)) {
            throw new BaseException(DATA_EXIST.getCode(), name + "该业务分类已存在！");
        }

        //没有设置pid的默认path就是 /
        business.setPath("/");
        if (StringUtils.isNotEmpty(pid)) {
            Optional<Business> repository = businessRepository.findById(pid);
            if (!repository.isPresent()) {
                throw new BaseException(DATA_NOT_EXIST.getCode(), "未找到对应父级业务信息！");
            }
            Business pbusiness = repository.get();
            String pname = pbusiness.getName();
            String path = pbusiness.getPath();
            business.setPname(pname);
            business.setPath(path + pid + "/");
        }

        //处理排序，为同级最后一个
        List<Business> allByPid = businessRepository.findAllByPid(pid);
        business.setSort(allByPid.size());

        String pname = business.getPname();

        //创建基础字段的索引
        List<BusinessField> fields = business.getFields();
        check(fields);
        creatIndex(fields, name, pname);

        businessRepository.save(business);

        logService.save("业务分类", name, OPERATION_ADD);

        List<String> orgIds = businessDTO.getOrgIds();
        permissionService.create(id, orgIds);


        return business;
    }


    /**
     * 检查是否有存在的key
     * @param fields
     */
    public void check(List<BusinessField> fields) {
        if (CollUtil.isEmpty(fields)) {
            return;
        }
        List<String> keys = new ArrayList<>();

        fields.stream().forEach(businessField -> {
            String key = businessField.getKey();
            if (keys.contains(key)) {
                throw new BaseException(DATA_EXIST.getCode(), "字段 " + key + "已存在，请确认后重试！");
            }else {
                keys.add(key);
            }
        });
    }


    @Override
    public Business update(UpdateBusinessDTO updateBusinessDTO, String id) {

        Business business = get(id);
        String name = business.getName();
        String pname = business.getPname();

        Integer sort = updateBusinessDTO.getSort();
        if (sort != null) {
            business.setSort(sort);
        }

        List<BusinessField> fields = updateBusinessDTO.getFields();
        if (CollUtil.isNotEmpty(fields)) {
            check(fields);
            try {
                creatIndex(fields,name,pname);
            } catch (UncategorizedElasticsearchException e) {
                log.error("更新索引出错:{}", e.getMessage());
                log.info("开始重建索引.....");
                IndexCoordinates index = getIndex(business.getId());
                updateIndex(fields, index);
            }
            business.setFields(fields);
        }

        //是否修改过指定字段
        Boolean isModify = false;

        String idCardFieldName = updateBusinessDTO.getIdCardFieldName();
        if (StringUtils.isNotBlank(idCardFieldName)) {
            String idCardFieldName1 = business.getIdCardFieldName();
            if (!idCardFieldName.equals(idCardFieldName1)) {
                business.setIdCardFieldName(idCardFieldName);
                isModify = true;
            }

        }

        String occurredTime = updateBusinessDTO.getOccurredTime();
        String occurredTime1 = business.getOccurredTime();
        if (!occurredTime.equals(occurredTime1)) {
            business.setOccurredTime(occurredTime);
            isModify = true;
        }


        String uniqueFieldName = updateBusinessDTO.getUniqueFieldName();
        String uniqueFieldName1 = business.getUniqueFieldName();
        if (!uniqueFieldName.equals(uniqueFieldName1)) {
            business.setUniqueFieldName(uniqueFieldName);
            isModify = true;
        }

        String orgFieldName = updateBusinessDTO.getOrgFieldName();
        String orgFieldName1 = business.getOrgFieldName();
        if (!orgFieldName.equals(orgFieldName1)) {
            business.setOrgFieldName(orgFieldName);
            isModify = true;
        }

        Business save = businessRepository.save(business);

        if (isModify) {
            documentService.updateHistoricalData(id);
        }

        return save;
    }

    @Override
    public Boolean sort(String id, Integer offset) {

        //实现方法 https://www.jianshu.com/p/9ee708e43ebf

        Business business = get(id);
        Integer sort = business.getSort();  //当前序号
        String pid = business.getPid();

        //新索引
        int newSort = sort + offset;
        business.setSort(newSort);
        businessRepository.save(business);


        //新位置大于老位置 范围内+1
        if (newSort > sort) {
            List<Business> allByPidAndSortBetween = businessRepository.findAllByPidAndSortBetween(pid, sort, newSort);
            allByPidAndSortBetween.stream().filter(business1 -> !business1.getId().equals(id)).forEach(business1 -> {
                Integer sort1 = business1.getSort();
                business1.setSort(sort1 - 1);
                businessRepository.save(business1);
            });


        } else if (newSort < sort) {    //范围内-1
            List<Business> allByPidAndSortBetween = businessRepository.findAllByPidAndSortBetween(pid, newSort, sort);
            allByPidAndSortBetween.stream().filter(business1 -> !business1.getId().equals(id)).forEach(business1 -> {
                Integer sort1 = business1.getSort();
                business1.setSort(sort1 + 1);
                businessRepository.save(business1);
            });
        }

        return true;
    }


    /**
     * 创建索引
     * @param fields 所有字段
     * @param name 业务分类中文名称
     * @param pname 父级业务分类中文名称 用来查询这个分类下面的所有数据
     */
    public void creatIndex(List<BusinessField> fields, String name,String pname) {

        String pinyin = PinyinUtil.getPinyin(name, "");
        String ppinyin = "";
        if (StringUtils.isNotBlank(pname)) {
            ppinyin = PinyinUtil.getPinyin(pname, "") + "_";
        }

        boolean indexExists = elasticsearchRestTemplate.indexExists(INDEX_PREFIX + ppinyin  + pinyin);
        if (!indexExists) {
            elasticsearchRestTemplate.createIndex(INDEX_PREFIX + ppinyin + pinyin);
        }

        IndexCoordinates indexCoordinates = QueryUtil.createIndex(name, pname);

        Map<String, Object> mapping = createMapping(fields);

        boolean putMapping = elasticsearchRestTemplate.putMapping(indexCoordinates, mapping);

        if (!putMapping) {
            throw new BaseException(ERROR.getCode(), "创建映射失败");
        }

        //把映射放到redis里面
        stringRedisTemplate.opsForValue().set(REDIS_PREFIX + INDEX_PREFIX + ppinyin + "_" + pinyin, name, 10000, TimeUnit.SECONDS);

    }


    /**
     * 创建映射
     * @param fields
     * @return
     */
    public Map<String, Object> createMapping(List<BusinessField> fields) {

        Map<String, Object> mapping = new HashMap<>();
        Map<String, Object> properties = new HashMap<>();

        if (CollUtil.isNotEmpty(fields)) {
            fields.stream().forEach(field -> {
                String key = field.getKey();
                //校验key不能有特殊字符
                if (key.contains("/")) {
                    throw new BaseException(BAD_REQUEST.getCode(), "属性名中不能存在 / 请替换成其他符号");
                }
                String type = field.getType();
                Map<String, Object> map = processingType(type);
                properties.put(key, map);
            });
        }

        //添加默认字段
        properties.put("create_org_id", processingType("string"));
        properties.put("create_org_name", processingType("string"));
        properties.put(ATTRIBUTION_ORG_ID, processingType("string")); //数据归属机构id
        properties.put(ATTRIBUTION_ORG_NAME, processingType("string"));//数据归属机构名称
        properties.put(ATTRIBUTION_ORG_NAME, processingType("string"));//数据归属机构名称
        properties.put(ATTRIBUTION_ORG_NAMES, processingType("string"));
        properties.put(OCCURRED_TIME, processingType("date"));//发生时间
        properties.put(ID_CARD, processingType("string"));//数据归属身份证号
        properties.put("create_user_id", processingType("string"));
        properties.put("create_user_name", processingType("string"));
        properties.put("create_time", processingType("date"));

        mapping.put("properties", properties);
        return mapping;
    }


    /**
     * 转移索引
     * @param fields
     * @param oldName
     * @param newName
     */
    public void transferIndex(List<BusinessField> fields,String oldName,String newName) {

        //新索引
        boolean indexExists = elasticsearchRestTemplate.indexExists(newName);
        if (!indexExists) {
            elasticsearchRestTemplate.createIndex(newName);
        }

        IndexCoordinates coordinates = IndexCoordinates.of(newName);
        Map<String, Object> mapping = createMapping(fields);

        boolean putMapping = elasticsearchRestTemplate.putMapping(coordinates, mapping);
        if (!putMapping) {
            throw new BaseException(ERROR.getCode(), "创建映射失败");
        }

        log.info("转移数据到新索引.....");
        //转移索引
        //删除索引的逻辑是直接把原来的索引名字改掉
        ReindexRequest reindexRequest = new ReindexRequest();
        reindexRequest.setSourceIndices(oldName);
        reindexRequest.setDestIndex(newName);
        reindexRequest.setRefresh(true);
        try {
            BulkByScrollResponse reindex = client.reindex(reindexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("转移索引失败");
        }
        log.info("转移完成,删除旧索引");

        //删除旧索引
        boolean deleteIndex = elasticsearchRestTemplate.deleteIndex(oldName);
        if (deleteIndex) {
            log.info("删除旧索引成功");
        }else {
            log.error("旧索引删除失败");
        }
    }


    @Override
    public void updateIndex(List<BusinessField> fields,IndexCoordinates indexCoordinates) {

        log.info("更新索引开始......");
        String indexName = indexCoordinates.getIndexName();

        //新索引
        String newIndexName = UPDATE_INDEX_PREFIX + indexName;
        //转移到新索引
        transferIndex(fields, indexName, newIndexName);
        //转回来
        transferIndex(fields, newIndexName, indexName);

    }

    @Override
    public List<Business> listByPid(String pid) {

        List<Business> allByPid = businessRepository.findAllByPid(pid);
        return allByPid;
    }

    @Override
    public Business get(String id) {
        Optional<Business> repository = businessRepository.findById(id);
        if (!repository.isPresent()) {
            throw new BaseException(DATA_NOT_EXIST.getCode(), "未找到该业务详情!");
        }

        Business business = repository.get();

        return business;
    }

    @Override
    public IndexCoordinates getIndex(String id) {
        Business business = get(id);
        String name = business.getName();
        String pname = business.getPname();
        IndexCoordinates indexCoordinates = QueryUtil.createIndex(name, pname);
        return indexCoordinates;
    }

    @Override
    public Boolean del(String id) {
        Business business = get(id);
        logService.save("业务分类", business.getName(),OPERATION_DEL);

        IndexCoordinates index = getIndex(id);

        String indexName = index.getIndexName();

        String newIndexName = indexName.replace(INDEX_PREFIX, DEL_INDEX_PREFIX);

        //删除索引的逻辑是直接把原来的索引名字改掉
        ReindexRequest reindexRequest = new ReindexRequest();
        reindexRequest.setSourceIndices(index.getIndexName());
        reindexRequest.setDestIndex(newIndexName);
        reindexRequest.setRefresh(true);

        try {
            BulkByScrollResponse reindex = client.reindex(reindexRequest, RequestOptions.DEFAULT);

            BulkByScrollTask.Status status = reindex.getStatus();

            //转移完成之后删除原索引
            boolean deleteIndex = elasticsearchRestTemplate.deleteIndex(indexName);

        } catch (IOException e) {
            e.printStackTrace();
            log.error("删除索引失败");
        }

        //删除业务下面的所有模板
        templateRepository.deleteAllByBusinessId(id);
        //删除相关权限
        permissionService.delByBusinessId(id);

        businessRepository.deleteById(id);

        return true;
    }

    @Override
    public List<Business> list() {

        Iterable<Business> repositoryAll = businessRepository.findAll();
        ArrayList<Business> businesses = Lists.newArrayList(repositoryAll);

        return businesses;
    }

    @Override
    public List<GroupVO> getFieldGroup(String businessId, String fieldName,Map<String, Object> param) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        BoolQueryBuilder qb = QueryBuilders.boolQuery();



        qb.filter(new TermsQueryBuilder(ATTRIBUTION_ORG_NAMES +".keyword", UserThreadLocal.getOrg()));

        //全局关键词
        String searchStr = MapUtil.getStr(param, "search");
        if (StringUtils.isNotEmpty(searchStr)) {
            //精确匹配  https://blog.csdn.net/hereiskxm/article/details/47979013
            MultiMatchQueryBuilder type = new MultiMatchQueryBuilder(searchStr).type(MultiMatchQueryBuilder.Type.PHRASE).slop(0);
            qb.filter(type);
        }

        Business business = get(businessId);
        //处理字段类型
        List<BusinessField> fields = business.getFields();
        HashMap<String, Object> map = new HashMap<>();
        fields.stream().forEach(field1->{
            String key = field1.getKey();
            String type = field1.getType();
            map.put(key, type);
        });

        if (param != null) {
            Set<Map.Entry<String, Object>> entries = param.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                String key = entry.getKey();
                //排除这几个自定义的字段
                if ("search,sort,order,pageSize,pageNum,archiveId".contains(key)) {
                    continue;
                }
                Object value = entry.getValue();
                //时间格式 中间用~分隔
                if ((value + "").contains("~")) {
                    String string = value.toString();
                    String[] split = string.split("~");
                    qb.filter(QueryBuilders.rangeQuery(key).gte(split[0]));
                    qb.filter(QueryBuilders.rangeQuery(key).lte(split[1]));
                }else {

                    String orgFieldName = business.getOrgFieldName();

                    //如果用指定单位字段查询就在特定字段里面取查
                    if (key.equals(orgFieldName)) {

                        BoolQueryBuilder qb2 = QueryBuilders.boolQuery();
                        qb2.should(new TermsQueryBuilder(ATTRIBUTION_ORG_NAMES + ".keyword", value));
                        qb2.should(QueryBuilders.termQuery(key + ".keyword", value));
                        qb.filter(qb2);
                    }else {
                        if ("list".equals(map.get(key))) {
                            qb.filter(QueryBuilders.termQuery(key + ".keyword", value));
                        }else {
                            qb.filter(new QueryStringQueryBuilder("\"" + value + "\"").field(key).defaultOperator(Operator.AND));
                        }
                    }

                }

            }
        }
        queryBuilder.withQuery(qb);

        TermsAggregationBuilder builder = AggregationBuilders.terms("fieldName").field(fieldName +".keyword");
        queryBuilder.addAggregation(builder);
        NativeSearchQuery build = queryBuilder.build();
        build.setMaxResults(0);

        //存档id
        String archiveId = MapUtil.getStr(param, "archiveId");
        IndexCoordinates index = getIndex(businessId);
        if (StringUtils.isNotBlank(archiveId)) {
            Archive archive = archiveService.get(archiveId);
            String indexName = archive.getIndexName();
            index = IndexCoordinates.of(indexName);
        }

        SearchHits<Object> search = elasticsearchRestTemplate.search(build, Object.class, index);
        Aggregations aggregations = search.getAggregations();
        ParsedStringTerms businessName = aggregations.get("fieldName");

        List<ParsedStringTerms.ParsedBucket> buckets = (List<ParsedStringTerms.ParsedBucket>) businessName.getBuckets();

        List<GroupVO> collect = buckets.stream().map(bucket -> {
            String keyAsString = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            GroupVO groupVO = new GroupVO();

            groupVO.setKey(keyAsString);
            groupVO.setNum(docCount);

            return groupVO;
        }).collect(Collectors.toList());


        return collect;
    }

    @Override
    public List<String> getFieldValue(String businessId, String fieldName, String archiveId) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();

        qb.filter(new TermsQueryBuilder(ATTRIBUTION_ORG_NAMES +".keyword", UserThreadLocal.getOrg()));

        queryBuilder.withQuery(qb);

        TermsAggregationBuilder builder = AggregationBuilders.terms("fieldName").field(fieldName +".keyword");
        queryBuilder.addAggregation(builder);
        NativeSearchQuery build = queryBuilder.build();
        build.setMaxResults(0);

        IndexCoordinates index = getIndex(businessId);
        if (StringUtils.isNotBlank(archiveId)) {
            Archive archive = archiveService.get(archiveId);
            String indexName = archive.getIndexName();
            index = IndexCoordinates.of(indexName);
        }


        SearchHits<Object> search = elasticsearchRestTemplate.search(build, Object.class, index);
        Aggregations aggregations = search.getAggregations();
        ParsedStringTerms businessName = aggregations.get("fieldName");

        List<ParsedStringTerms.ParsedBucket> buckets = (List<ParsedStringTerms.ParsedBucket>) businessName.getBuckets();

        List<String> collect = buckets.stream().map(bucket -> {
            String keyAsString = bucket.getKeyAsString();

            return keyAsString;
        }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public List<GroupVO> getOrgGroup(String indexName, Long startTime, Long endTime, String orgId) {

        if (StringUtils.isBlank(orgId)) {
            orgId = UserThreadLocal.getOrgId();
        }

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        qb.filter(new QueryStringQueryBuilder("true").field("is_doc").defaultOperator(Operator.AND));
        qb.filter(QueryBuilders.rangeQuery("create_time").gte(startTime));
        qb.filter(QueryBuilders.rangeQuery("create_time").lt(endTime));
        Organization organization = organizationService.get(orgId);

        qb.filter(new TermsQueryBuilder(ATTRIBUTION_ORG_NAMES +".keyword", organization.getOrgName()));

        TermsAggregationBuilder builder = AggregationBuilders.terms("orgName").field(ATTRIBUTION_ORG_NAME + ".keyword");
        queryBuilder.withQuery(qb);
        queryBuilder.addAggregation(builder);
        NativeSearchQuery build = queryBuilder.build();
        build.setMaxResults(0);

        String pinyin = PinyinUtil.getPinyin(indexName, "");
        IndexCoordinates indexCoordinates = IndexCoordinates.of(INDEX_PREFIX + pinyin);

        SearchHits<Object> search = elasticsearchRestTemplate.search(build, Object.class, indexCoordinates);
        Aggregations aggregations = search.getAggregations();
        if (aggregations == null) {
            return null;
        }

        ParsedStringTerms orgName = aggregations.get("orgName");

        List<ParsedStringTerms.ParsedBucket> buckets = (List<ParsedStringTerms.ParsedBucket>) orgName.getBuckets();

        List<GroupVO> collect = buckets.stream().map(bucket -> {
            String keyAsString = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            GroupVO groupVO = new GroupVO();
            groupVO.setNum(docCount);
            groupVO.setKey(keyAsString);

            return groupVO;
        }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public List<GroupVO> getAssociatedOrgGroup(String businessId, Long startTime, Long endTime) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        qb.filter(new QueryStringQueryBuilder("true").field("is_doc").defaultOperator(Operator.AND));

        Business business = get(businessId);
        String occurredTime = business.getOccurredTime();
        //默认统计时间字段
        String timeFieldName = CREATE_TIME;
        if (StringUtils.isNotBlank(occurredTime)) {
            timeFieldName = occurredTime;
        }

        //指定单位字段
        String orgFieldName = business.getOrgFieldName();
        String orgFieldNameQuery = CREATE_ORG_NAME;
        if (StringUtils.isNotBlank(orgFieldName)) {
            orgFieldNameQuery = ATTRIBUTION_ORG_NAME;
        }

        qb.filter(QueryBuilders.rangeQuery(timeFieldName).gte(startTime));
        qb.filter(QueryBuilders.rangeQuery(timeFieldName).lt(endTime));

        qb.filter(new TermsQueryBuilder(ATTRIBUTION_ORG_NAMES +".keyword", UserThreadLocal.getOrg()));

        TermsAggregationBuilder builder = AggregationBuilders.terms("orgName").field(orgFieldNameQuery + ".keyword");
        queryBuilder.withQuery(qb);
        queryBuilder.addAggregation(builder);
        NativeSearchQuery build = queryBuilder.build();
        build.setMaxResults(0);

        IndexCoordinates index = getIndex(businessId);

        SearchHits<Object> search = elasticsearchRestTemplate.search(build, Object.class, index);
        Aggregations aggregations = search.getAggregations();
        if (aggregations == null) {
            return null;
        }

        ParsedStringTerms orgName = aggregations.get("orgName");

        List<ParsedStringTerms.ParsedBucket> buckets = (List<ParsedStringTerms.ParsedBucket>) orgName.getBuckets();

        List<GroupVO> collect = buckets.stream().map(bucket -> {
            String keyAsString = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            GroupVO groupVO = new GroupVO();
            groupVO.setNum(docCount);
            groupVO.setKey(keyAsString);

            return groupVO;
        }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public List<GroupVO> getGroup(String businessId, String fieldName, Long startTime, Long endTime, String orgId) {

        if (StringUtils.isBlank(orgId)) {
            orgId = UserThreadLocal.getOrgId();
        }

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        qb.filter(new QueryStringQueryBuilder("true").field("is_doc").defaultOperator(Operator.AND));

        Business business = get(businessId);
        String occurredTime = business.getOccurredTime();
        //默认统计时间字段
        String timeFieldName = CREATE_TIME;
        if (StringUtils.isNotBlank(occurredTime)) {
            timeFieldName = occurredTime;
        }

        qb.filter(QueryBuilders.rangeQuery(timeFieldName).gte(startTime));
        qb.filter(QueryBuilders.rangeQuery(timeFieldName).lt(endTime));

        Organization organization = organizationService.get(orgId);
        qb.filter(new TermsQueryBuilder(ATTRIBUTION_ORG_NAMES +".keyword", organization.getOrgName()));


        TermsAggregationBuilder builder = AggregationBuilders.terms("groupName").field(fieldName + ".keyword");
        queryBuilder.withQuery(qb);
        queryBuilder.addAggregation(builder);
        NativeSearchQuery build = queryBuilder.build();
        build.setMaxResults(0);

        IndexCoordinates index = getIndex(businessId);

        SearchHits<Object> search = elasticsearchRestTemplate.search(build, Object.class, index);
        Aggregations aggregations = search.getAggregations();
        if (aggregations == null) {
            return null;
        }

        ParsedStringTerms orgName = aggregations.get("groupName");

        List<ParsedStringTerms.ParsedBucket> buckets = (List<ParsedStringTerms.ParsedBucket>) orgName.getBuckets();

        List<GroupVO> collect = buckets.stream().map(bucket -> {
            String keyAsString = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            GroupVO groupVO = new GroupVO();
            groupVO.setNum(docCount);
            groupVO.setKey(keyAsString);

            return groupVO;
        }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public List<OrgStatDateVO> getDateGroup(String indexName) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        qb.filter(new QueryStringQueryBuilder("true").field("is_doc").defaultOperator(Operator.AND));

        TermsAggregationBuilder builder = AggregationBuilders.terms("date").field("create_time.keyword");
        queryBuilder.withQuery(qb);
        queryBuilder.addAggregation(builder);
        NativeSearchQuery build = queryBuilder.build();
        build.setMaxResults(0);

        String pinyin = PinyinUtil.getPinyin(indexName, "");
        IndexCoordinates indexCoordinates = IndexCoordinates.of(INDEX_PREFIX + pinyin);

        SearchHits<Object> search = elasticsearchRestTemplate.search(build, Object.class, indexCoordinates);
        Aggregations aggregations = search.getAggregations();
        if (aggregations == null) {
            return null;
        }

        ParsedStringTerms orgName = aggregations.get("date");

        List<ParsedStringTerms.ParsedBucket> buckets = (List<ParsedStringTerms.ParsedBucket>) orgName.getBuckets();

        List<OrgStatDateVO> collect = buckets.stream().map(bucket -> {
            String keyAsString = bucket.getKeyAsString();


            //处理时区
            DateTime dateTime = DateUtil.date(Long.parseLong(keyAsString));
            DateTime dateTime1 = DateUtil.offsetHour(dateTime, 8);
            String dateStr = DateUtil.date(dateTime1).toDateStr();
            long docCount = bucket.getDocCount();
            OrgStatDateVO orgStatDateVO = new OrgStatDateVO();
            orgStatDateVO.setDate(dateStr);

            GroupVO groupVO = new GroupVO();
            groupVO.setNum(docCount);
            orgStatDateVO.setGroups(Lists.newArrayList(groupVO));

            return orgStatDateVO;
        }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public Long getDocNum(String businessId) {

        Business business = get(businessId);
        String pname = business.getPname();
        String name = business.getName();

        IndexCoordinates index = QueryUtil.createIndex(name, pname);

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        qb.filter(new QueryStringQueryBuilder("true").field("is_doc").defaultOperator(Operator.AND));

        queryBuilder.withQuery(qb);

        long count = elasticsearchRestTemplate.count(queryBuilder.build(), index);


        return count;
    }

    @Override
    public List<BusinessVO> getBusinessVTree() {

        List<BusinessVO> children = getChildren(null);

        return children;
    }

    @Override
    public Business getByName(String name) {
        Business busines = null;
        List<Business> business = businessRepository.findAllByName(name);
        if (CollUtil.isNotEmpty(business)) {
            //必须有父级id和有业务字段的
            if (business.size() > 1) {
                List<Business> collect = business.stream().filter(business1 -> {
                    boolean notBlank = StringUtils.isNotBlank(business1.getPid());
                    List<BusinessField> fields = business1.getFields();
                    return notBlank && CollUtil.isNotEmpty(fields);
                }).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(collect)) {
                    busines = collect.get(0);
                }
            } else if (business.size() == 1) {
                busines = business.get(0);
            }
        }else {
            throw new BaseException(DATA_NOT_EXIST.getCode(), "未找到该业务信息！");
        }

        return busines;
    }


    /**
     * 获取子业务
     * @param pid
     * @return
     */
    public List<BusinessVO> getChildren(String pid) {

        List<Business> businesses = businessRepository.findAllByPidOrderByCreateTimeAsc(pid);

        List<BusinessVO> collect = businesses.stream().map(business -> {

            BusinessVO businessVO = modelMapper.map(business, BusinessVO.class);
            String id = businessVO.getId();
            businessVO.setChildren(getChildren(id));

            return businessVO;
        }).collect(Collectors.toList());

        return collect;

    }


    /**
     * 处理类型映射
     * @param type  字段类型 string number date time list
     * @return
     */
    public Map<String, Object> processingType(String type) {

        Map<String, Object> typeMap = new HashMap<>();

        if ("string,text,list,file,image".contains(type)) {
            Map<String, Object> fields =  new HashMap<>();

            Map<String, Object> keyword =  new HashMap<>();
            keyword.put("type", "keyword");
            keyword.put("ignore_above", 256);

            typeMap.put("type", "text");
            typeMap.put("fields", fields);
            fields.put("keyword", keyword);

        } else if ("number".equals(type)) {
            typeMap.put("type", "long");
        } else if ("date,month,time".contains(type)) {
            typeMap.put("type", "date");
            typeMap.put("format", "yyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis");
        } else {
            typeMap.put("type", type);
        }

        return typeMap;

    }


}
